package edu.fiu.accesslog.parser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import edu.fiu.accesslog.SingleAccessLog;

/**
 * @author Hasan Mahmud @date May 25, 2012
 *
 */
public class ApacheParserImpl implements IParser
{

        /*
         * %a - Remote IP address %A - Local IP address %b - Bytes sent,
         * excluding HTTP headers, or '-' if zero %h - Remote host name (or IP
         * address if resolveHosts is false) %H - Request protocol %l - Remote
         * logical username from identd (always returns '-') %m - Request method
         * (GET, POST, etc.) %p - Local port on which this request was received
         * %q - Query string (prepended with a '?' if it exists) %r - First line
         * of the request (method and request URI) %s - HTTP status code of the
         * response %S - User session ID %t - Date and time, in Common Log
         * Format %u - Remote user that was authenticated (if any), else '-' %U
         * - Requested URL path %v - Local server name %D - Time taken to
         * process the request, in millis %T - Time taken to process the
         * request, in seconds %I - current request thread name (can compare
         * later with stacktraces)
         */
        private List<String> formatLst = new ArrayList<String>();
        private String format = null;
        private String delimiter = " ";
        private final static String NONE = "NONE";

        /**
         * Tokenize the given format with the delimiter provided.
         *
         * @param format
         */
        public ApacheParserImpl()
        {
        }

        /*
         * )
         * @see org.web.acesslog.parser.IParser#setFormat(java.lang.String)
         */
        public void setFormat(String format)
        {
                this.format = format;
                populateFormatLst();
        }

        /**
         *
         */
        private void populateFormatLst()
        {
                //System.out.println("### Formatter Impl : "+ this.getClass().getCanonicalName());
                System.out.println("### Format : " + format);
                StringTokenizer tokenizer = new StringTokenizer(format, delimiter);
                String token;
                while (tokenizer.hasMoreTokens())
                {
                        token = tokenizer.nextToken();
                        // System.out.println(token);
                        this.formatLst.add(token);
                }
        }

        /*
         * (non-Javadoc)
         *
         * @see com.acess.log.parser.Formatter#parse(java.lang.String)
         */
        @Override
        public SingleAccessLog parse(String inputLine, String includeExtn)
        {
                StringTokenizer inputTokeLine = new StringTokenizer(inputLine, delimiter);
                Iterator<String> formatItr = formatLst.iterator();
                SingleAccessLog access = new SingleAccessLog();
                boolean isIncludeExtn = false;
                //int i = 0;
                String token, format;
                while (inputTokeLine.hasMoreTokens() && formatItr.hasNext())
                {


                        token = inputTokeLine.nextToken();
                        //System.out.println(token);
                        format = formatItr.next();
                        if ("%a".equals(format))
                        {
                                access.setRemoteIpAddr(token);
                        }
                        else if ("%A".equals(format))
                        {
                                access.setLocalIpAddr(token);
                        }
                        else if ("%b".equals(format))
                        {

                                try
                                {
                                        access.setBytesSent(Long.parseLong(token));
                                }
                                catch (Exception e)
                                {
                                        access.setBytesSent(0);
                                }
                        }
                        else if ("%h".equals(format))
                        {
                                access.setRemoteHostName(token);
                        }
                        else if ("%H".equals(format))
                        {
                                access.setRequestProtocol(token);
                        }
                        else if ("%l".equals(format))
                        {
                                access.setRemoteLogicalUserName(token);
                        }
                        else if ("%m".equals(format))
                        {
                                access.setRequestMethod(token);
                        }
                        else if ("%p".equals(format))
                        {
                                access.setLocalPort(token);
                        }
                        else if ("%q".equals(format))
                        {
                                access.setQueryString(token);
                        }
                        else if ("%r".equals(format))
                        {
                                //Specific handling for url string.
                                access.setRequestMethod(token);
                                String url = inputTokeLine.nextToken();
                                if (url.contains("?"))
                                {
                                        url = url.substring(0, url.indexOf("?"));
                                        access.setFirstLineOfRequest(url);
                                }
                                else
                                {
                                        access.setFirstLineOfRequest(url);
                                }
                                if (includeExtn == null || NONE.equalsIgnoreCase(includeExtn))
                                {
                                        //Do nothing.
                                }
                                else
                                {
                                        StringTokenizer includeExtnTokenizer = new StringTokenizer(includeExtn, ",");
                                        while (includeExtnTokenizer.hasMoreTokens())
                                        {
                                                String extension = includeExtnTokenizer.nextToken();
                                                if (url.contains(extension))
                                                {
                                                        isIncludeExtn = true;
                                                }
                                        }
                                }
                                access.setRequestProtocol(inputTokeLine.nextToken());
                        }
                        else if ("\"%r\"".equals(format))
                        {
                                //Specific handling for url string.
                                access.setRequestMethod(token.substring(1));
                                String url = inputTokeLine.nextToken();
                                if (url.contains("?"))
                                {
                                        url = url.substring(0, url.indexOf("?"));
                                        access.setFirstLineOfRequest(url);
                                }
                                else
                                {
                                        access.setFirstLineOfRequest(url);
                                }

                                String requestProtocol = inputTokeLine.nextToken();
                                int index = requestProtocol.indexOf("\"");
                                if (index < 0)
                                {
                                        access.setRequestProtocol(requestProtocol);
                                }
                                else
                                {
                                        access.setRequestProtocol(requestProtocol.substring(0, requestProtocol.indexOf("\"")));
                                }

                        }
                        else if ("%s".equals(format))
                        {
                                access.setHttpStatusCode(token);
                        }
                        else if ("%S".equals(format))
                        {
                                access.setUserSessionId(token);
                        }
                        else if ("%t".equals(format))
                        {
                                //Specific handling for this required since [ is used with space for timezone offset
                                access.setDateTime(token + inputTokeLine.nextToken());
                        }
                        else if ("%u".equals(format))
                        {
                                access.setRemoteUser(token);
                        }
                        else if ("%U".equals(format))
                        {
                                access.setRequestedUrlPath(token);
                        }
                        else if ("%v".equals(format))
                        {
                                access.setLocalServerName(token);
                        }
                        else if ("%D".equals(format))
                        {
                                access.setTimeTakeInMilliSec(token);
                        }
                        else if ("%T".equals(format))
                        {
                                access.setTimeTakeInSec(token);
                        }
                        else if ("%I".equals(format))
                        {
                                access.setCurrentRequestThreadName(token);
                        }
                        else if (format.equals("\"%{Referer}i\""))
                        {
                                access.setReferer(token);
                        }
                        else if (format.equals("\"%{User-agent}i\""))
                        {

                                if (!isEmpty(token))
                                {
                                        StringBuilder uAgent = new StringBuilder(token);

                                        for (; inputTokeLine.hasMoreTokens();)
                                        {
                                                uAgent.append(" ");
                                                uAgent.append(inputTokeLine.nextToken());
                                        }
                                        uAgent.deleteCharAt(0);
                                        uAgent.deleteCharAt(uAgent.length() - 1);
                                        //System.out.println("Token: "+uAgent.toString());
                                        access.setUserAgentString(uAgent.toString());
                                }

                        }

                }


                return access;
        }

        private boolean isEmpty(String value)
        {
                if (value.equals("-") || value.equals("\"-\""))
                {
                        return true;
                }
                return false;
        }

        public String getFormat()
        {
                return format;
        }
}